using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Windows.Forms;

namespace az
{
    public partial class Form1 : Form
    {
        private bool isAddVertex = false;
        private bool isAddEdge = false;
        private PictureBox pictureVertex;
        private Graph graph;
        private bool isEdgePainting = false;
        private int v1;
        private Point v1_p1;
        private Point v1_p2;
        private int maxNr;
        private string stringV2="";
        private List<Label> labelList = new List<Label>();
        private Graph graphClone;
       
        public Form1()
        {
            InitializeComponent();
            this.MinimumSize=new Size(949, 590);
            this.WindowState = FormWindowState.Maximized;
            this.listBox1.Size = new Size(this.panel3.Size.Width, this.panel3.Size.Height-32);
            this.panel4.Size = new Size(this.panel2.Size.Width, 21);
            this.panel4.Location=new Point(0, this.panel2.Height-21);
            this.panel2.MouseMove += new MouseEventHandler(panel2_MouseMove);
            this.panel2.MouseLeave += new EventHandler(panel2_MouseLeave);
            this.panel2.MouseEnter += new EventHandler(panel2_MouseEnter);
            this.panel2.MouseClick += new MouseEventHandler(panel2_MouseClick);
            this.panel2.Paint += new PaintEventHandler(panel2_Paint);
            this.panel2.BackColor = Color.White;
            pictureVertex = new PictureBox();
            pictureVertex.Size = new Size(16, 16);
            pictureVertex.Image = global::az.Properties.Resources.vertex;
            DisabledButton();
            this.button1.Enabled = true;
            this.button2.Enabled = true;
            this.button9.Enabled = true;
            this.checkBox1.Checked = false;
            this.textBox1.Visible = false;
            this.label5.Visible = false;
            for (int i = 1; i < 11; i++)
                this.comboBox1.Items.Add(i);
            this.comboBox1.SelectedIndex = 1;
            this.textBox2.Text = 10.ToString();
            this.textBox3.Text = 15.ToString();
            graph=new Graph();
        }

        void panel2_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            int width = int.Parse(this.comboBox1.Items[this.comboBox1.SelectedIndex].ToString());
            foreach (Edge ed in graph.listEdge)
            {
                g.DrawLine(new Pen(Color.FromArgb(ed.color),width), ed.vertex1.point, ed.vertex2.point);
            }
            if(isEdgePainting)
            {
                g.DrawLine(new Pen(Color.Black), v1_p1, v1_p2);
                this.label2.Text = "Połącz wierzchołek: " + v1.ToString() + stringV2;
            }
            else
            {
                this.label2.Text = "";
            }
         }

        void panel2_MouseClick(object sender, MouseEventArgs e)
        {
            if (isAddVertex)
            {
                graph.countVertex++;
                graph.listVertex.Add(new Vertex(graph.countVertex, e.X+1+8,e.Y+1+8));

                Ver p = new Ver(graph.countVertex);
                p.Location = new Point(e.X + 1, e.Y + 1);
                this.panel2.Controls.Add(p);

                Label l = new Label();
                l.AutoSize = true;
                l.Location = new Point(e.X, e.Y-13);
                l.Text = graph.countVertex.ToString();
                this.panel2.Controls.Add(l);

                p.MouseEnter += new EventHandler(p_MouseEnter);
                p.MouseLeave += new EventHandler(p_MouseLeave);
                p.MouseClick += new MouseEventHandler(p_MouseClick);
            }

            if (isAddEdge)
            {
                isEdgePainting = false;
                this.Refresh();
            }
        }

        void p_MouseClick(object sender, MouseEventArgs e)
        {
            
            if(isAddEdge)
            {
                
                Vertex vertex1=new Vertex(0,0,0);
                Vertex vertex2=new Vertex(0,0,0);
                bool canCreate = true;
                Ver v = (Ver) sender;
                if(!isEdgePainting)
                {
                    isEdgePainting = true;
                    v1 = v.number;
                    v1_p1=new Point(v.Location.X + 8, v.Location.Y + 8);
                    v1_p2 = new Point(v.Location.X + 8, v.Location.Y + 8);
                }
                else
                    if(isEdgePainting)
                    {
                        isEdgePainting = false;
                        foreach (Vertex ver in graph.listVertex)
                        {
                            if (ver.number == v1)
                            {
                                foreach (Vertex vn in ver.listVertexNeighbor)
                                {
                                    if (vn.number == v.number)
                                    {
                                        canCreate = false;
                                    }
                                }
                            }
                        }
                        if(canCreate)
                        {
                            foreach (Vertex ver in graph.listVertex)
                            {
                                if (ver.number == v.number)
                                {
                                    vertex2 = ver;
                                }
                            }
                            foreach (Vertex ver in graph.listVertex)
                            {
                                if (ver.number == v1)
                                {
                                    vertex1 = ver;
                                }
                            }
                            if (vertex1 == vertex2)
                            {
                                this.Refresh();
                                return;
                            }
                            Random random = new Random(System.DateTime.Now.Millisecond);
                            int color = Color.FromArgb(random.Next(0, 200), random.Next(0, 200), random.Next(0, 200)).ToArgb();
                            graph.countEdge++;
                            Edge ed = new Edge(graph.countEdge, vertex1, vertex2, color);
                            graph.listEdge.Add(ed);
                            vertex1.listVertexNeighbor.Add(vertex2);
                            vertex1.countVertexNeighbour++;
                            vertex1.listEdgeConnected.Add(ed);
                            vertex2.listVertexNeighbor.Add(vertex1);
                            vertex2.countVertexNeighbour++;
                            vertex2.listEdgeConnected.Add(ed);
                            if(vertex1.number < vertex2.number)
                                this.listBox1.Items.Add(graph.countEdge+": "+"w"+vertex1.number+" - w"+vertex2.number);
                            else
                                this.listBox1.Items.Add(graph.countEdge + ": " + "w" + vertex2.number + " - w" + vertex1.number);
                        }
                    }
                this.Refresh();
            }
        }

        void p_MouseLeave(object sender, EventArgs e)
        {
            if (isAddEdge)
            {
                this.Cursor = Cursors.Default;
                if(isEdgePainting)
                {
                    stringV2 = "";
                    this.Refresh();
                }
            }
        }

        void p_MouseEnter(object sender, EventArgs e)
        {
            if (isAddEdge)
            {
                this.Cursor = Cursors.Hand;
                if (isEdgePainting)
                {
                    Ver v = (Ver)sender;
                    stringV2 = " z " + v.number.ToString();
                    this.Refresh();
                }
            }
        }

        void panel2_MouseEnter(object sender, EventArgs e)
        {
            if (isAddVertex)
            {
                this.panel2.Controls.Add(pictureVertex);
            }
            if (isAddEdge)
            {
                
            }
        }   

        void panel2_MouseLeave(object sender, EventArgs e)
        {
            if (isAddVertex)
            {
                this.panel2.Controls.Remove(pictureVertex);
            }
            if (isAddEdge)
            {
                
            }
        }

        private void DisabledButton()
        {
            this.button9.Enabled = false;
            this.button8.Enabled = false;
            this.button7.Enabled = false;
            this.button6.Enabled = false;
            this.button5.Enabled = false;
            this.button4.Enabled = false;
            this.button3.Enabled = false;
            this.button2.Enabled = false;
            this.button1.Enabled = false;
        }

        private void EnabledButton()
        {
            this.button9.Enabled = true;
            this.button8.Enabled = true;
            this.button7.Enabled = true;
            this.button6.Enabled = true;
            this.button5.Enabled = true;
            this.button4.Enabled = true;
            this.button3.Enabled = true;
            this.button2.Enabled = true;
            this.button1.Enabled = true;
        }

        void panel2_MouseMove(object sender, MouseEventArgs e)
        {
            if(isAddVertex)
            {
                pictureVertex.Location = new Point(e.X+1, e.Y+1);
            }
            if (isAddEdge)
            {
                if (isEdgePainting)
                {
                    v1_p2 = new Point(e.X, e.Y);
                    this.Refresh();
                }
            }

        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.label5.Visible = false;
            DeleteColour();
            DisabledButton();
            this.button8.Enabled = true;
            isAddVertex = true;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.label5.Visible = false;
            DeleteColour();
            DisabledButton();
            this.button8.Enabled = true;
            isAddEdge = true;
        }

        private void button8_Click(object sender, EventArgs e)
        {
            this.label5.Visible = false;
            EnabledButton();
            this.button8.Enabled = false;
            isAddEdge = false;
            isAddVertex = false;
            isEdgePainting = false;
            this.Refresh();
        }

        private void DeleteGraph()
        {
            graph = new Graph();
            this.panel2.Controls.Clear();
            this.panel2.Controls.Add(panel4);
            this.listBox1.Items.Clear();
            this.Refresh();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            this.label5.Visible = false;
            DeleteColour();
            DeleteGraph();
            DisabledButton();
            this.button1.Enabled = true;
            this.button2.Enabled = true;
            this.button9.Enabled = true;
        }

        private void DeleteColour()
        {
            graph.isColour = false;
            foreach (Vertex ver in graph.listVertex)
                ver.numberColor = 0;
            foreach (Label l in labelList)
                this.panel2.Controls.Remove(l);
            labelList.Clear();
        }

        private void button7_Click(object sender, EventArgs e)
        {
            this.label5.Visible = false;
            DeleteColour();
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (this.checkBox1.Checked)
                this.textBox1.Visible = true;
            else
                this.textBox1.Visible = false;
        }

        private bool CheckNr()
        {
            try
            {
                maxNr = int.Parse(this.textBox1.Text);
            }
            catch (Exception)
            {
                return false;
            }
            if (maxNr > 0)
                return true;
            return false;

        }

        private void CreateLabelColour()
        {
            for (int i = 0; i < graph.listVertex.Count; i++)
            {
                Label l = new Label();
                l.ForeColor = Color.FromArgb(0, 0, 255);
                l.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
                l.AutoSize = true;
                l.Location = new Point(graph.listVertex[i].point.X - 8, graph.listVertex[i].point.Y + 8);
                l.Text = graph.listVertex[i].numberColor.ToString();
                labelList.Add(l);
                this.panel2.Controls.Add(l);
            }
            Console.WriteLine();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            this.label5.Visible = false;
            DeleteColour();
            if (this.checkBox1.Checked)
            {
                if (CheckNr())
                {
                    LargestFirst largestFirst = new LargestFirst();
                    largestFirst.LargestFirstAlgoritm(graph, true, maxNr);
                    CreateLabelColour();
                }
                else
                {
                    SetLabel("Niepoprawna liczba");
                }
            }
            else
            {
                LargestFirst largestFirst = new LargestFirst();
                largestFirst.LargestFirstAlgoritm(graph, false, 0);
                CreateLabelColour();
            }
        }

        private void button5_Click(object sender, EventArgs e)
        {
            this.label5.Visible = false;
            DeleteColour();
            if (this.checkBox1.Checked)
            {
                if (CheckNr())
                {
                    graphClone = CloneObject<Graph>(graph);
                    SmallestLast smallestLast = new SmallestLast();
                    smallestLast.SmallestLastAlgoritm(graph, graphClone, true, maxNr);
                    CreateLabelColour();
                }
                else
                {
                    SetLabel("Niepoprawna liczba");
                }
            }
            else
            {
                graphClone = CloneObject<Graph>(graph);
                SmallestLast smallestLast = new SmallestLast();
                smallestLast.SmallestLastAlgoritm(graph, graphClone, false, 0);
                CreateLabelColour();
            }
        }

        private void button6_Click(object sender, EventArgs e)
        {
            this.label5.Visible = false;
            DeleteColour();
            if (this.checkBox1.Checked)
            {
                if (CheckNr())
                {
                    DSatur dSatur = new DSatur();
                    dSatur.DStaturAlgoritm(graph, true, maxNr);
                    CreateLabelColour();
                }
                else
                {
                    SetLabel("Niepoprawna liczba");
                }
            }
            else
            {
                DSatur dSatur = new DSatur();
                dSatur.DStaturAlgoritm(graph, false, 0);
                CreateLabelColour();
            }
        }

        private void SetLabel(string s)
        {
            this.label5.Visible = true;
            this.label5.Text = s;
            this.label5.Location = new Point((this.panel1.ClientSize.Width - this.label5.Size.Width) / 2, 234);
        }

        private void button9_Click(object sender, EventArgs e)
        {
            this.label5.Visible = false;
            DeleteColour();
            DeleteGraph();
            int vertexCount;
            int edgeCount;
            try
            {
                vertexCount = int.Parse(textBox2.Text);
                edgeCount = int.Parse(textBox3.Text);
            }
            catch (Exception)
            {
                SetLabel("Niepoprawna liczba");
                return;
            }
            if (vertexCount > 200)
            {
                SetLabel("Max liczba wierzchołków 200");
                return;
            }
            if (edgeCount > (vertexCount * (vertexCount - 1)) / 2)
            {
                SetLabel("Za dużo krawędzi");
                return;
            }
            EnabledButton();
            this.button8.Enabled = false;
            //////////////////////////////////////////
            graph = testy.GenerateRandom(vertexCount, edgeCount);
            int middle = Math.Min(panel2.Width,panel2.Height);
            middle = middle/2;
            int radius = middle - 30;
            testy.GenerateCoords(graph,middle,radius);
            this.panel2.Invalidate();
            foreach (Vertex v in graph.listVertex)
            {
                Ver p = new Ver(v.number);
                p.Location = new Point(v.point.X - 8, v.point.Y - 8);
                this.panel2.Controls.Add(p);
                p.MouseEnter += new EventHandler(p_MouseEnter);
                p.MouseLeave += new EventHandler(p_MouseLeave);
                p.MouseClick += new MouseEventHandler(p_MouseClick);
                Label l = new Label();
                l.AutoSize = true;
                l.Location = new Point(v.point.X - 9, v.point.Y - 22);
                l.Text = v.number.ToString();
                this.panel2.Controls.Add(l);
            }
            Random random = new Random(System.DateTime.Now.Millisecond);
            foreach (Edge ed in graph.listEdge)
            {
                int color = Color.FromArgb(random.Next(0, 200), random.Next(0, 200), random.Next(0, 200)).ToArgb();
                ed.color = color;
                if (ed.vertex1.number < ed.vertex2.number)
                    this.listBox1.Items.Add(ed.number + ": " + "w" + ed.vertex1.number + " - w" + ed.vertex2.number);
                else
                    this.listBox1.Items.Add(ed.number + ": " + "w" + ed.vertex2.number + " - w" + ed.vertex1.number);
            }
            /////////////////////////////////////////////
            /*
            for(int i = 0; i < vertexCount; i++)
            {
                if(!RandomVertex())
                {
                    DeleteGraph();
                    DisabledButton();
                    this.button1.Enabled = true;
                    this.button2.Enabled = true;
                    this.button9.Enabled = true;
                    SetLabel("Nie udało się utworzyć grafu");
                    return;
                }
            }
            for (int i = 0; i < edgeCount; i++)
            {
                if(!RandomEdge(vertexCount))
                {
                    DeleteGraph();
                    DisabledButton();
                    this.button1.Enabled = true;
                    this.button2.Enabled = true;
                    this.button9.Enabled = true;
                    SetLabel("Nie udało się utworzyć grafu");
                    return;
                }
            }*/
            this.Refresh();
        }

        private bool RandomVertex()
        {
            int xr = 0, yr = 0, x = 0, y = 0, i = 0;
            bool good = false;
            while (true)
            {
                i++;
                Random r = new Random(System.DateTime.Now.Millisecond);
                x = xr = r.Next(10, this.panel2.ClientSize.Width - 10);
                y = yr = r.Next(23, this.panel2.ClientSize.Height - 31);
                while (x < this.panel2.ClientSize.Width - 10)
                {
                    bool error2 = false;
                    foreach (Vertex v2 in graph.listVertex)
                    {
                        if (x - v2.point.X < 32 && x - v2.point.X > -32 && y - v2.point.Y < 32 && y - v2.point.Y > -32)
                        {
                            error2 = true;
                            break;
                        }
                    }
                    if (error2)
                        x = x + 10;
                    else
                    {
                        good = true;
                        break;
                    }
                }
                if (!good)
                {
                    x = xr;
                    x = x - 10;
                    while (x > 10)
                    {
                        bool error2 = false;
                        foreach (Vertex v2 in graph.listVertex)
                        {
                            if (x - v2.point.X < 32 && x - v2.point.X > -32 && y - v2.point.Y < 32 && y - v2.point.Y > -32)
                            {
                                error2 = true;
                                break;
                            }
                        }
                        if (error2)
                            x = x - 10;
                        else
                        {
                            good = true;
                            break;
                        }
                    }
                }
                if (!good)
                {
                    x = xr;
                    while (y < this.panel2.ClientSize.Height - 31)
                    {
                        bool error2 = false;
                        foreach (Vertex v2 in graph.listVertex)
                        {
                            if (x - v2.point.X < 32 && x - v2.point.X > -32 && y - v2.point.Y < 32 && y - v2.point.Y > -32)
                            {
                                error2 = true;
                                break;
                            }
                        }
                        if (error2)
                            y = y + 10;
                        else
                        {
                            good = true;
                            break;
                        }
                    }
                }
                if (!good)
                {
                    x = xr;
                    y = yr;
                    y = y - 10;
                    while (y > 23)
                    {
                        bool error2 = false;
                        foreach (Vertex v2 in graph.listVertex)
                        {
                            if (x - v2.point.X < 32 && x - v2.point.X > -32 && y - v2.point.Y < 32 && y - v2.point.Y > -32)
                            {
                                error2 = true;
                                break;
                            }
                        }
                        if (error2)
                            y = y - 10;
                        else
                        {
                            good = true;
                            break;
                        }
                    }
                }
                if (i == 10000)
                    return false;
                if (good)
                    break;
            }
            graph.countVertex++;
            graph.listVertex.Add(new Vertex(graph.countVertex, x, y));

            Ver p = new Ver(graph.countVertex);
            p.Location = new Point(x - 8, y - 8);
            this.panel2.Controls.Add(p);

            Label l = new Label();
            l.AutoSize = true;
            l.Location = new Point(x - 9, y - 22);
            l.Text = graph.countVertex.ToString();
            this.panel2.Controls.Add(l);

            p.MouseEnter += new EventHandler(p_MouseEnter);
            p.MouseLeave += new EventHandler(p_MouseLeave);
            p.MouseClick += new MouseEventHandler(p_MouseClick);
            return true;
        }

        private bool RandomEdge(int v)
        {
            int v1, v2, vr1, vr2, i = 0;
            Vertex vx1 = null, vx2 = null;
            bool good = false;
            while (true)
            {
                i++;
                if (i == 10000)
                    return false;
                Random r = new Random(System.DateTime.Now.Millisecond);
                v1 = vr1 = r.Next(1, v);
                v2 = vr2 = r.Next(1, v);
                if (v1 == v2)
                    continue;
                while (v2 <= v)
                {
                    bool error = false;
                    foreach (Vertex ver in graph.listVertex)
                    {
                        if (ver.number == v1)
                        {
                            foreach (Vertex vn in ver.listVertexNeighbor)
                            {
                                if (vn.number == v2)
                                {
                                    error = true;
                                    break;
                                }
                            }
                            if (error)
                            {
                                v2++;
                                if (v1 == v2)
                                    v2++;
                                break;
                            }
                            else
                            {
                                good = true;
                                break;
                            }
                        }
                      
                    }
                    if (good)
                        break;
                }
                if (!good)
                {
                    v1 = vr1;
                    v2 = vr2;
                    while (v2 > 0)
                    {
                        bool error = false;
                        foreach (Vertex ver in graph.listVertex)
                        {
                            if (ver.number == v1)
                            {
                                foreach (Vertex vn in ver.listVertexNeighbor)
                                {
                                    if (vn.number == v2)
                                    {
                                        error = true;
                                        break;
                                    }
                                }
                                if (error)
                                {
                                    v2--;
                                    if (v1 == v2)
                                        v2--;
                                    break;
                                }
                                else
                                {
                                    good = true;
                                    break; 
                                }
                            }

                        }
                        if (good)
                            break;
                    }
                }
                if (!good)
                {
                    v1 = vr1;
                    v2 = vr2;
                    while (v1 <= v)
                    {
                        bool error = false;
                        foreach (Vertex ver in graph.listVertex)
                        {
                            if (ver.number == v1)
                            {
                                foreach (Vertex vn in ver.listVertexNeighbor)
                                {
                                    if (vn.number == v2)
                                    {
                                        error = true;
                                        break;
                                    }
                                }
                                if (error)
                                {
                                    v1++;
                                    if (v1 == v2)
                                        v1++;
                                    break;
                                }
                                else
                                {
                                    good = true;
                                    break;
                                }
                            }

                        }
                        if (good)
                            break;
                    }
                }
                if (!good)
                {
                    v1 = vr1;
                    v2 = vr2;
                    while (v1 > 0)
                    {
                        bool error = false;
                        foreach (Vertex ver in graph.listVertex)
                        {
                            if (ver.number == v1)
                            {
                                foreach (Vertex vn in ver.listVertexNeighbor)
                                {
                                    if (vn.number == v2)
                                    {
                                        error = true;
                                        break;
                                    }
                                }
                                if (error)
                                {
                                    v1--;
                                    if (v1 == v2)
                                        v1--;
                                    break;
                                }
                                else
                                {
                                    good = true;
                                    break;
                                }
                            }

                        }
                        if (good)
                            break;
                    }
                }
                if(v1==v2)
                    continue;
                if (good)
                    break;
            }
            foreach (Vertex ver in graph.listVertex)
            {
                if (ver.number == v1)
                {
                    vx1 = ver;
                }
            }
            foreach (Vertex ver in graph.listVertex)
            {
                if (ver.number == v2)
                {
                    vx2 = ver;
                }
            }
            Random random = new Random(System.DateTime.Now.Millisecond);
            int color = Color.FromArgb(random.Next(0, 200), random.Next(0, 200), random.Next(0, 200)).ToArgb();
            graph.countEdge++;
            Edge ed = new Edge(graph.countEdge, vx1, vx2, color);
            graph.listEdge.Add(ed);
            vx1.listVertexNeighbor.Add(vx2);
            vx1.countVertexNeighbour++;
            vx1.listEdgeConnected.Add(ed);
            vx2.listVertexNeighbor.Add(vx1);
            vx2.countVertexNeighbour++;
            vx2.listEdgeConnected.Add(ed);
            if (vx1.number < vx2.number)
                this.listBox1.Items.Add(graph.countEdge + ": " + "w" + vx1.number + " - w" + vx2.number);
            else
                this.listBox1.Items.Add(graph.countEdge + ": " + "w" + vx2.number + " - w" + vx1.number);
            return true;
        }
        
        public static T CloneObject<T>(T item)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
                bf.Serialize(ms, item);
                ms.Seek(0, SeekOrigin.Begin);
                return (T)bf.Deserialize(ms);
            }
        }
    }
}
